import {Set, Map} from "immutable";
import {Sequelize, Model, DataTypes, ModelAttributes} from 'sequelize'
import {Resource} from "./resource";
import {StringType} from "./definition";

// 数据源适配器   database   filesystem   相关业务系统
// entity   origin
abstract class Adapter {

  register?(resource: Resource): any

  group?(entityName: string, queries: object): Promise<any>

  find?(entityName: string, queries: object): Promise<any[]>

  findById?(entityName: string, queries: object): Promise<any>

  save?(entityName: string, queries: object): Promise<any>

  delete?(entityName: string, queries: object): Promise<any>

}

export default Adapter;


export class SequelizeAdapter extends Adapter {

  sequelize: Sequelize;

  models = Set<typeof AnyModel>();

  getModel = (() => {
    let models: Set<typeof AnyModel>;
    let cache: Map<string, typeof AnyModel>;

    const refresh = () => {
      if (!models || !models.equals(this.models)) {
        models = this.models;
        cache = Map();
        models.forEach((model) => {
          cache = cache.set(model.name, model);
        });
      }
    };

    return (modelName: string) => {
      refresh();
      const Entity = cache.get(modelName);
      if (!Entity) {
        throw new Error('The model is not yet defined')
      }
      return Entity;
    }
  })();

  constructor(sequelize: Sequelize) {
    super();
    this.sequelize = sequelize;
  }

  register(resource: Resource) {

    const AnonymousEntity = class extends Model {};

    const attributes = resource.fields.reduce((prev, field): ModelAttributes => ({
      ...prev,
      [field.name]: {
        type: getType(field.type),
      },
    }), {});

    AnonymousEntity.init(attributes, {
      modelName: resource.name,
      sequelize: this.sequelize,
      timestamps: false,
    });

    this.models = this.models.add(AnonymousEntity);
  }

  find(entityName: string, queries: object): Promise<any[]> {
    const Entity = this.getModel(entityName);
    return Entity.findAll(queries);
  }

  findById(entityName: string, queries: { id: string }): Promise<any> {
    const Entity = this.getModel(entityName);
    return Entity.findByPk(queries.id);
  }

  save(entityName: string, queries: object): Promise<any> {
    const Entity = this.getModel(entityName);
    return Entity.create(queries);
  }

  delete(entityName: string, queries: object): Promise<any> {
    const Entity = this.getModel(entityName);
    return Promise.resolve()
  }

}


class AnyModel extends Model {}

const getType = (function makeTypes() {
  let types = Map();
  types = types.set(StringType, DataTypes.STRING);

  return function (type: any) {
    return types.get(type)
  }
})();
